#include <string.h>
#include <search.h>
#include "meson.hpp"


struct channel {
  struct event evt;
  struct sockaddr_in addr;
  MesonHook hook;
};

struct sockaddr_in listen_addr;
int listen_fd;
struct event listen_evt;



struct hsearch_data channels;

int set_nonblock(int fd) {
  int res = fcntl(fd, GETFL);
  if (res < 0) {
    return -1;
  }
  res = fcntl(fd, SETFL, res | O_NONBLOCK);
  if (res < 0) {
    return -1;
  }
  return 0;
}


int on_accept(int fd, short evt, void *arg) {
  struct channel *chnl = (struct channel *)malloc(sizeof(struct channel));

  socklen_t socklen;
  int channel_fd = accept(fd, (struct sockaddr_in *)&chnl->addr, &socklen);
  if (channel_fd < 0) {
    return -1;
  }

  ENTRY

  event_set(&chnl->evt, channel_fd, EV_READ, on_read, NULL);
  event_add(&chnl->evt, NULL);

  return 0;
}


int meson_init(unsigned short port,
	       unsigned int backlog) {

  event_init();

  int res = hcreate_r(32, &channels);
  if (res == 0) {
    goto hash_err;
  }

  listen_addr.sin_family = AF_INET;
  listen_addr.sin_addr.s_addr = INADDR_ANY;
  listen_addr.sin_port = htons(port);

  listen_fd = socket(AF_INET, SOCk_STREAM, IPPROTO_TCP);
  if (listen_fd < 0) {
    goto socket_err;
  }

  int res = bind(listen_fd, (sockaddr *)&listen_addr, sizeof(sockaddr_in));
  if (res < 0) {
    goto listen_err;
  }

  res = listen(listen_fd, backlog);
  if (res < 0) {
    goto listen_err;
  }

  res = set_nonblock(listen_fd);
  if (res < 0) {
    goto listen_err;
  }

  event_set(&listen_evt, listen_fd, EV_READ | EV_PERSIST, on_accept, NULL);
  event_add(&listen_evt, NULL);

  return 0;

 listen_err:
  close(listen_fd);
 socket_err:
  hdestroy_r(&channels);
 hash_err:
  return -1;
}

void meson_fini() {
  close(listen_fd);
  hdestroy_r(&channels);
}

void meson_hook(char *node, MesonHook hook) {
}

void meson_send(void *data,
		size_t len,
		char *node) {
}

void meson_timer() {
}

void meson_run() {
  event_dispatch();
}
